iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
Vibe Coding

做一個團購後端,順便搞懂那些事系列 第 25 篇

Day 25|多台機器時,WebSocket 通知送不到

  • 分享至 

  • xImage
  •  

這個服務設計上原本就假設會有多個實例同時運作,MySQL 與 Redis 由所有實例共用。假設現在有機器A、機器B,Alice 的 WebSocket 連在機器A,訂單到期後,各台排程會從Redis搶著認領;她參與的那張訂單,這次由機器B搶到並完成扣款,結算完成後,機器B要通知 Alice。

伺服器以 convertAndSendToUser 指定收件人,再由WebSocket的broker找到這位使用者的連線並送達。

問題:WebSocket 連線只存在單台機器

假設這次由機器 B 完成扣款,接著呼叫 convertAndSendToUser("alice", ...) 發送通知,convertAndSendToUser() 只能從目前機器B的 WebSocket 連線中尋找 Alice,可是Alice的連線在機器 A,機器 B 並不知道這條連線存在,因此通知無法送出。

https://ithelp.ithome.com.tw/upload/images/20261008/20168667V1H5gH3FME.png

在這個通知送出失敗的情境下,程式不會拋出例外、也不寫error log,WebSocket 的連線與訂閱狀態都只存在建立連線的那台 JVM(機器A),其他機器看不到。

解決方法:先廣播,再由各機器尋找連線

機器 B 完成扣款後,不直接把通知送給 WebSocket,而是先呼叫 publish(),把「通知 alice」這件事發布到 Redis:

public void publish(String userId, String destination, Object payload) {
    NotificationEnvelope envelope = new NotificationEnvelope(userId, destination, payload);
    try {
        String json = objectMapper.writeValueAsString(envelope);
        redisTemplate.convertAndSend(CHANNEL, json);   // websocket:notifications
    } catch (JsonProcessingException e) {
        log.error("Failed to serialize notification envelope for user={}, destination={}", ...);
    }
}

這裡的 userId 是收件人;destination 是 WebSocket 要送到的目的地;payload 則是通知內容,這些資料會被包進 NotificationEnvelope,再透過 CHANNEL 發布到 Redis。

下一步Redis Pub/Sub 會接著把這則訊息送給所有訂閱這個 CHANNEL 的機器,所以機器 A、B 都會收到同一個 NotificationEnvelope。

每台機器收到 Redis 訊息後,再交給本機的 WebSocket broker:

@Override
public void onMessage(Message message, byte[] pattern) {
    try {
        NotificationEnvelope envelope =
                objectMapper.readValue(new String(message.getBody()), NotificationEnvelope.class);
        messagingTemplate.convertAndSendToUser(
                envelope.getUserId(), envelope.getDestination(), envelope.getPayload());
    } catch (Exception e) {
        log.warn("Failed to relay notification from Redis: {}", e.getMessage());
    }
}

這裡才會真正把 Redis 收到的訊息交給本機的 WebSocket broker,機器A 有 Alice 的連線,所以 convertAndSendToUser() 可以送達;機器B沒有Alice的連線,就不會送出任何東西。讓每台機器都收到通知,再由各自的 WebSocket broker 判斷這個使用者是否連在自己這台機器上。

限制:Pub/Sub 不保存訊息

假使使用者當下離線或正在重連,這則通知就會直接消失,沒有補送機制。因此,目前結算通知功能只能做到盡力而為,不保證送達。


上一篇
Day 24|推播不能跟著交易一起送
下一篇
Day 26|通知會漏、會亂序,訊息裡該放什麼?
系列文
做一個團購後端,順便搞懂那些事 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言